#ifndef xpack_graphic_private_rander_helper
#define xpack_graphic_private_rander_helper
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::graphic_private_rander_helper::inc
#include"algo/sort.hpp"
#include"configure/limit.hpp"
#include"define/base_type.hpp"
#include"graphic/color/rgb.hpp"
#include"graphic/rander_wrapper.hpp"
#include"macro/xdebug_fail.hpp"
#include"macro/xdefer.hpp"
#include"macro/xexport.hpp"
#include"macro/xmodify.hpp"
#include"math/point.hpp"
#include"memop/copy.hpp"
#include"memop/swap.hpp"
#include"utils/bits_array.hpp"
#include"utils/mix_buffer.hpp"
#pragma pop_macro("xuser")

namespace mixc::graphic_private_rander_helper{
    struct dummy_rander{
        void operator()(auto, auto, auto){}
    };
}

namespace mixc::graphic_private_rander_helper::origin{
    using mix_buffer        = inc::mix_buffer<inc::pt2wx, inc::limit::graphic_private_rander_helper_mix_buffer>;
    using bits_array        = inc::bits_array<inc::limit::graphic_private_rander_helper_bits_array>;
    using inc::thickness_mode_t;
    using inc::texture_mode_t;

    struct rander_helper{
        #define xgen(NAME)                                                                              \
            template<class rander_t>                                                                    \
            static void set_ ## NAME(rander_t const & rander, auto const & value){                      \
                if constexpr (inc::is_rander_wrapper<rander_t>){                                        \
                    rander->NAME = value;                                                               \
                }                                                                                       \
            }                                                                                           \
            template<class rander_t>                                                                    \
            static auto get_ ## NAME(rander_t const & rander){                                          \
                if constexpr (inc::is_rander_wrapper<rander_t>){                                        \
                    return rander->NAME;                                                                \
                }                                                                                       \
                else{                                                                                   \
                    using color_t =                                                                     \
                        ::mixc::graphic_rander_wrapper                                                  \
                        ::inner::color_type_of<decltype(rander)>;                                       \
                    constexpr auto dummy = inc::rander_wrapper<dummy_rander, color_t>();                \
                    return dummy->NAME;                                                                 \
                }                                                                                       \
            }

        #define xarg_gen(NAME)      xgen(NAME)
        #include"graphic/xgen.foreach_rander_prop.hpp"
        #undef  xgen

        template<class rander_t, class draw_t>
        static void thickening(rander_t const & rander, draw_t const & draw){
            using buff          = mix_buffer;

            auto old_thickness  = rander_helper::get_thickness(rander);
            xdefer{
                rander_helper::set_thickness(rander, old_thickness);
            };

            rander_helper::set_thickness(rander, 1);
            buff obuffer;
            buff ibuffer;

            // 图形外圈
            auto orander        = xrander(auto x, auto y){
                auto item       = inc::pt2wx(x, y);
                obuffer.push(item);
            };

            // 图形内圈
            auto irander        = xrander(auto x, auto y){
                auto item       = inc::pt2wx(x, y);
                ibuffer.push(item);
            };

            // 先获取所有绘制点
            draw(orander, irander);

            // 分开存放 outside、inside 点集，用于解决点重合时绘制异常问题
            // 将点排序，一串连续的点属于同一行，依次经过 outside 点、inside 点、inside 点、outside 点
            // 只需要填充 outside 到 inside 点和 inside 点到 outside 点之间的点就行了
            inc::sort::heap(ibuffer);
            inc::sort::heap(obuffer);
            auto ilength    = ibuffer->length();
            auto olength    = obuffer->length();
            auto oprev      = obuffer[0];
            auto ocurr      = oprev;
            auto icurr      = ibuffer[0];
            oprev->x        = oprev->x - 1;

            for(uxx i = 0, j = 0; i < olength; i++, oprev = ocurr){
                // 外圈连续的像素，直接绘制
                if (ocurr = obuffer[i]; oprev->x + 1 >= ocurr->x){
                    rander(ocurr->x, ocurr->y);
                    continue;
                }

                if (j >= ilength){
                    continue;
                }
                else{
                    icurr = ibuffer[j];
                }

                // 移除内圈重复像素
                while(j + 1 < ilength){
                    if (auto inext = ibuffer[j + 1]; inext->x == icurr->x and inext->y == icurr->y){
                        icurr = inext;
                        j += 1;
                    }
                    else{
                        break;
                    }
                }

                // ocurr->y < icurr->y 或者 ocurr->y > icurr->y
                // 两个 outside 像素点间没有 inside 像素，直接绘制一条连续的直线
                // outside 是将 inisde 包裹起来的
                if (ocurr->y != icurr->y){
                    while(oprev->x <= ocurr->x){
                        rander(oprev->x, oprev->y);
                        oprev->x = oprev->x + 1;
                    }
                    continue;
                }

                // 外圈左边到内圈左边
                while(oprev->x <= icurr->x){
                    rander(oprev->x, oprev->y);
                    oprev->x = oprev->x + 1;
                }

                // 内圈自身像素
                while(j + 1 < ilength){
                    if (auto inext = ibuffer[++j]; icurr->y == inext->y){
                        rander(icurr->x, icurr->y);
                        icurr = inext;
                    }
                    else{
                        break;
                    }
                }

                // 内圈右边到外圈右边
                while(icurr->x <= ocurr->x){
                    rander(icurr->x, icurr->y);
                    icurr->x = icurr->x + 1;
                }
            }
        }

        template<class rander_t, class draw_t>
        static void texturing(rander_t const & rander, draw_t const & draw){
            using buff          = mix_buffer;
            using ba_t          = bits_array;

            if constexpr (inc::is_rander_wrapper<rander_t>){
                if (rander_helper::get_texture_mode(rander) == inc::texture_mode_t::none){
                    return;
                }

                buff buffer;
                ba_t bmp;
                auto left       = uxx(bmp->length);
                auto right      = uxx{};
                auto randerx    = xrander(auto x, auto y, auto){
                    auto item   = inc::pt2wx(x, y);
                    buffer.push(item);

                    if (left > x){
                        left    = x;
                    }

                    if (right < x){
                        right   = x;
                    }
                };

                if (draw(randerx); right >= bmp->length){
                    return;
                }

                inc::sort::heap(buffer);

                auto prev       = buffer[0];
                auto curr       = prev;
                auto first      = true;
                prev->x         = prev->x - 1;

                for(uxx i = 0, length = buffer->length; i < length; i++, prev = curr){
                    if (curr = buffer[i]; prev->y != curr->y){
                        bmp.reset_range(co{left, curr->x - 1});
                        bmp.reset_range(oo{prev->x + 1, right});
                        first   = true;
                        continue;
                    }

                    if (prev->x == curr->x){
                        continue;
                    }

                    // 从 y 轴低地址往高地址绘制
                    // 如果当前行遇到横线，就取和上一行相反的属性值，比如上一行 x = 10~20 这个区间内属于可绘制的范围
                    // 那么当前行存在一条 x = 10~20 范围的直线，那么直线之上的空间假定为不可绘制的空间
                    // 假如上一行 x = 10~20 这个区间是不可绘制的空间，也就是图形外，那么假定此行以上的 x = 10~20 这个范围就是可以绘制的空间
                    // 实际上也就是一条图形内外的分割线
                    if (prev->x + 1 >= curr->x){
                        bmp[curr->x] = ! bmp[curr->x];
                    }
                    // 此处表示 prev->x 和 curr->x 存在跨度，至少两个像素点之间隔着一个点
                    // 如果是当前行的第一个区间，就忽略之后的规则，默认直接绘制
                    // 否则需要确认一下此区间是否实在图形内，此时就需要查找当前区间的上一行的两个端点是否为 true
                    else if (first or bmp[prev->x + 1] or bmp[curr->x]){
                        auto length = curr->x - prev->x - 1;
                        bmp.set_range(co{prev->x + 1, prev->x + 1 + length});
                        rander->texturing(prev->x + 1, prev->y, length);
                    }

                    // 一行只使用一次
                    first = false;
                }
            }
        }
    };
}

#endif

xexport_space(mixc::graphic_private_rander_helper::origin)
